package com.ncmmall.application.business.live;

import cn.hutool.core.date.DateUtil;
import com.ncmmall.application.business.live.tencentcloud.im.IMUtils;
import com.feijin.commons.lang.ArithUtils;
import com.feijin.commons.lang.Requires;
import com.feijin.commons.webs.SyncLock;
import com.ncmmall.domain.business.live.LiveChannel;
import com.ncmmall.domain.business.live.LiveSession;
import com.ncmmall.domain.business.live.LiveSessionGoods;
import com.ncmmall.domain.business.live.LiveSessionGoodsRepository;
import com.ncmmall.domain.business.live.LiveSessionRecord;
import com.ncmmall.domain.business.live.LiveSessionRecordRepository;
import com.ncmmall.domain.business.live.LiveSessionRedpacket;
import com.ncmmall.domain.business.live.LiveSessionRedpacketRepository;
import com.ncmmall.domain.business.live.LiveSessionRepository;
import com.ncmmall.domain.business.live.LiveSessionUserRedPacket;
import com.ncmmall.domain.business.live.LiveSessionUserRedpacketRepository;
import com.ncmmall.domain.business.live.QLiveSessionGoods;
import com.ncmmall.domain.security.user.WebUser;
import com.ncmmall.domain.security.user.WebUserRepository;
import com.ncmmall.querychannel.service.QueryChannelService;
import com.google.common.collect.ImmutableMap;
import lombok.RequiredArgsConstructor;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang.RandomStringUtils;
import org.apache.commons.lang.time.DateUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.sql.Timestamp;
import java.util.Date;
import java.util.List;
import java.util.Map;

/**
 * 直播场次管理
 *
 * @author likaikai
 * @date 2020-10-29 10:16:44
 */
@Service
@Transactional
@RequiredArgsConstructor
public class LiveSessionApplication {

    private final QueryChannelService queryChannelService;
    private final WebUserRepository userRepository;
    private final LiveSessionRepository liveSessionRepository;
    private final LiveSessionGoodsRepository liveSessionGoodsRepository;
    private final LiveSessionRedpacketRepository liveSessionRedpacketRepository;
    private final LiveSessionUserRedpacketRepository liveSessionUserRedpacketRepository;
    private final LiveSessionRecordRepository liveSessionRecordRepository;

    //播放域名格式：推流域名/app名称/流名称
    //示例 rtmp://live.likaikai.cn/tuotuo/1
    private static final String PUSH_DOMAIN = "rtmp://livepush.likaikai.cn";
    //播放域名
    private static final String PLAY_DOMAIN = "rtmp://live.likaikai.cn";
    //应用名称
    private static final String APP_NAME = "tuotuo";
    //防盗链key
    private static final String LIVE_KEY = "60155e87dddbffd19f4cf55de3c8f0ce";
    //回调key
    private static final String CALLBACK_KEY = "ujdV0PxiKfq9RHek97PT";

    /**
     *
     */
    public LiveSession findById(long id) {
        return liveSessionRepository.findOne(id);
    }


    /**
     * 生成推流地址
     *
     * @param streamName 房间id
     */
    private static String getPushUrl(String streamName) {
        String txTime = Long.toHexString(DateUtils.addDays(new Date(), 1).getTime() / 1000).toUpperCase();
        String txSecret = DigestUtils.md5Hex(LIVE_KEY + streamName + txTime);
        return PUSH_DOMAIN + "/" + APP_NAME + "/" + streamName + "?txSecret=" + txSecret + "&txTime=" + txTime;
    }

    private static String getPlayUrl(String streamName) {
        return PLAY_DOMAIN + "/" + APP_NAME + "/" + streamName;
    }

    /**
     * 检测录制回调的签名是否正确
     *
     * @param sign 签名 MD5(key + time)
     * @param time unix时间戳，超过10分钟则为无效消息
     */
    public static boolean checkRecordCallbackSign(String sign, long time) {
        if (time - DateUtil.currentSeconds() > 600) {
            return false;
        }
        return sign.equalsIgnoreCase(DigestUtils.md5Hex(CALLBACK_KEY + time));
    }

    /**
     * 开始直播
     *
     * @param anchor 主播
     * @param params 直播参数
     */
    public LiveSession start(WebUser anchor, LiveSession params) {

        Requires.isTrue(!anchor.isLiveStatus(), "无法开启新的直播");
        Requires.hasLength(anchor.getLiveName(), "直播间名称不能为空");
        Requires.hasLength(anchor.getLiveIntroduce(), "直播间介绍不能为空");
        Requires.hasLength(anchor.getLiveImage(), "直播间图片不能为空");

        List<LiveSessionGoods> sessionGoodsList = queryChannelService.findAll(QLiveSessionGoods.liveSessionGoods.anchor.id.eq(anchor.getId()).and(QLiveSessionGoods.liveSessionGoods.liveSession.isNull()), LiveSessionGoods.class);
//        for (LiveSessionGoods sessionGoods : sessionGoodsList) {
//            Requires.isTrue(sessionGoods.getCommonRedPacketPrice() > 0, "有商品未设置红包，请前往商品管理");
//            Requires.isTrue(sessionGoods.getCommonRedPacketHour() > 0, "有商品未设置红包，请前往商品管理");
//            Requires.isTrue(sessionGoods.getCommonRedPacketSec() > 0, "有商品未设置红包，请前往商品管理");
//            Requires.isTrue(sessionGoods.getVieRedPacketPrice() > 0, "有商品未设置红包，请前往商品管理");
//            Requires.isTrue(sessionGoods.getVieRedPacketHour() > 0, "有商品未设置红包，请前往商品管理");
//            Requires.isTrue(sessionGoods.getVieRedPacketSec() > 0, "有商品未设置红包，请前往商品管理");
//        }

        LiveSession session = new LiveSession();
        session.setLiveChannel(queryChannelService.findOne(LiveChannel.class, params.getLiveChannelId()));
        Requires.notNull(session.getLiveChannel(), "直播分类不存在");
        session.setAnchor(anchor);
        session.setName(anchor.getLiveName());
        session.setIntroduce(anchor.getLiveIntroduce());
        session.setImage(anchor.getLiveImage());
        session.setBeginTime(new Timestamp(System.currentTimeMillis()));
        liveSessionRepository.save(session);

        session.setPushUrl(getPushUrl(String.valueOf(session.getId())));
        session.setPlayUrl(getPlayUrl(String.valueOf(session.getId())));

        //创建直播群组
        String groupId = IMUtils.createGroup(session.getAnchor().getLiveID() + RandomStringUtils.randomNumeric(8));
        session.setImGroupId(groupId);
        liveSessionRepository.save(session);

        for (LiveSessionGoods sessionGoods : sessionGoodsList) {
            sessionGoods.setLiveSession(session);
        }
        liveSessionGoodsRepository.save(sessionGoodsList);

        anchor.setLiveSession(session);
        anchor.setLiveStatus(true);
        userRepository.save(anchor);

        return session;
    }

    /**
     * 结束直播
     *
     * @param anchor 直播
     */
    public void end(WebUser anchor) {

        LiveSession liveSession = anchor.getLiveSession();
        Requires.notNull(liveSession, "主播未开启直播");
        liveSession.setEndTime(new Timestamp(System.currentTimeMillis()));
        liveSessionRepository.save(liveSession);

        anchor.setLiveSession(null);
        anchor.setLiveStatus(false);
        userRepository.save(anchor);

        try {
            IMUtils.destroyGroup(liveSession.getImGroupId());
        } catch (IllegalArgumentException e) {
            //解散IM群组异常不影响直播正常结束
        }

    }

    /**
     * 精选/取消精选
     *
     * @param liveSession 直播场次
     */
    public void choiceness(LiveSession liveSession) {
        liveSession.setChoiceness(!liveSession.isChoiceness());
        liveSessionRepository.save(liveSession);
    }

    /**
     * 删除直播记录
     *
     * @param liveSession 直播场次
     */
    public void remove(LiveSession liveSession) {
        Requires.isTrue(liveSession.getEndTime() != null, "直播未结束，无法删除");
        liveSession.setRemove(true);
        liveSessionRepository.save(liveSession);
    }

    /**
     * 录制回调
     */
    public void recordCallback(LiveSessionRecord record) {
        liveSessionRecordRepository.save(record);
    }

    /**
     * 直播切换下一件商品
     *
     * @param anchor 主播
     */
    public LiveSessionGoods nextGoods(WebUser anchor) {

        LiveSession liveSession = anchor.getLiveSession();
        LiveSessionGoods goods = queryChannelService.queryOneEntity("select * from live_session_goods where live_session_id = :sid and status = false order by id limit 1",
                ImmutableMap.of("sid", liveSession.getId()), LiveSessionGoods.class);
        if (goods == null) {
            liveSession.setCurrentGoods(null);
            liveSession.setCurrentMaxRedpacket(0);
            liveSessionRepository.save(liveSession);
            return null;
        }

        liveSession.setCurrentGoods(goods);
        liveSession.setCurrentMaxRedpacket(ArithUtils.roundHalfUp(goods.getGoods().getDefaultPrice() * goods.getLiveGoodsAnchor().getLiveGoods().getGuaranteeScale(), 2));
        liveSessionRepository.save(liveSession);

        goods.setStatus(true);
        liveSessionGoodsRepository.save(goods);
        return goods;
    }

    /**
     * 直播间观看人数
     */
    public long look(LiveSession liveSession) {
        synchronized (SyncLock.getLock("live_session_look_" + liveSession.getId())) {
            Map<String, Object> map = queryChannelService.selectOneMap("select look_number from live_session where id = :id", ImmutableMap.of("id", liveSession.getId()));
            liveSession.setLookNumber(MapUtils.getLong(map, "look_number") + 1);
            liveSessionRepository.save(liveSession);
        }
        return liveSession.getLookNumber();
    }

    /**
     * 直播间点赞人数
     */
    public long like(LiveSession liveSession) {
        synchronized (SyncLock.getLock("live_session_like_" + liveSession.getId())) {
            Map<String, Object> map = queryChannelService.selectOneMap("select like_number from live_session where id = :id", ImmutableMap.of("id", liveSession.getId()));
            liveSession.setLikeNumber(MapUtils.getLong(map, "like_number") + 1);
            liveSessionRepository.save(liveSession);
        }
        return liveSession.getLikeNumber();
    }

    /**
     * 发送个人红包
     *
     * @param session 直播间
     * @param money   红包金额
     */
    public void sendSingleRedpacket(LiveSession session, WebUser user, double money) {

        Requires.isTrue(session.getEndTime() == null, "直播已结束");
        Requires.isTrue(session.getCurrentGoods() != null, "直播商品不存在");

        //发红包记录
        LiveSessionRedpacket sessionRedpacket = new LiveSessionRedpacket()
                .setLiveSession(session)
                .setType(1)
                .setCreateTime(new Timestamp(System.currentTimeMillis()))
                .setPrice(money)
                .setLiveSessionGoods(session.getCurrentGoods())
                .setLen(1)
                .setUser(user);
        liveSessionRedpacketRepository.save(sessionRedpacket);
    }

    /**
     * 获取个人红包
     */
    public void getSingleRedpacket(LiveSessionRedpacket sessionRedpacket, WebUser user) {

        Requires.isTrue(sessionRedpacket.getUser().getId() == user.getId() && sessionRedpacket.getSendLen() < sessionRedpacket.getLen(), "无法领取");
        checkRedpacketDraw(sessionRedpacket, user);

        synchronized (SyncLock.getLock(String.valueOf(sessionRedpacket.getId()))) {
            checkRedpacketDraw(sessionRedpacket, user);

            LiveSession session = sessionRedpacket.getLiveSession();
            LiveSessionUserRedPacket userRedPacket = new LiveSessionUserRedPacket()
                    .setLiveSession(session)
                    .setType(1)
                    .setUser(user)
                    .setCreateTime(sessionRedpacket.getCreateTime())
                    .setPrice(sessionRedpacket.getPrice())
                    .setLiveSessionGoods(session.getCurrentGoods())
                    .setLiveSessionRedpacket(sessionRedpacket);
            liveSessionUserRedpacketRepository.save(userRedPacket);

            sessionRedpacket.setSendLen(1);
            liveSessionRedpacketRepository.save(sessionRedpacket);
        }
    }

    /**
     * 检测红包是否已被领取
     *
     * @param sessionRedpacket 红包
     * @param user             用户
     */
    private void checkRedpacketDraw(LiveSessionRedpacket sessionRedpacket, WebUser user) {
        int count = queryChannelService.selectCount("select count(*) from live_session_user_redpacket where user_id = :uid and live_session_redpacket_id = :rid",
                ImmutableMap.of("uid", user.getId(), "rid", sessionRedpacket.getId()));
        Requires.isTrue(count == 0, "无法重复领取");
    }

    /**
     * 发送普通红包
     *
     * @param session 直播间
     * @param len     红包数量
     */
    public void sendCommonRedpacket(LiveSession session, int len) {

        Requires.isTrue(session.getEndTime() == null, "直播已结束");
        Requires.isTrue(session.getCurrentGoods() != null, "直播商品不存在");
        Requires.isTrue(session.getCurrentGoods().getCommonRedPacketPrice() > 0, "商品未设置普通红包");

        //发红包记录
        LiveSessionRedpacket sessionRedpacket = new LiveSessionRedpacket()
                .setLiveSession(session)
                .setType(2)
                .setCreateTime(new Timestamp(System.currentTimeMillis()))
                .setPrice(session.getCurrentGoods().getCommonRedPacketPrice())
                .setLiveSessionGoods(session.getCurrentGoods())
                .setLen(len)
                .setRedPacketHour(session.getCurrentGoods().getCommonRedPacketHour())
                .setEndTime(new Timestamp(session.getCurrentGoods().getVieRedPacketSec() * 1000 + System.currentTimeMillis()));
        liveSessionRedpacketRepository.save(sessionRedpacket);
    }

    /**
     * 发送抢购红包
     *
     * @param session 直播间
     * @param len     红包数量
     */
    public void sendVieRedpacket(LiveSession session, int len) {

        Requires.isTrue(session.getEndTime() == null, "直播已结束");
        Requires.isTrue(session.getCurrentGoods() != null, "直播商品不存在");
        Requires.isTrue(session.getCurrentGoods().getVieRedPacketPrice() > 0, "商品未设置抢购红包");

        //发红包记录
        LiveSessionRedpacket sessionRedpacket = new LiveSessionRedpacket()
                .setLiveSession(session)
                .setType(3)
                .setCreateTime(new Timestamp(System.currentTimeMillis()))
                .setPrice(session.getCurrentGoods().getVieRedPacketPrice())
                .setLiveSessionGoods(session.getCurrentGoods())
                .setLen(len)
                .setRedPacketHour(session.getCurrentGoods().getVieRedPacketHour())
                .setEndTime(new Timestamp(System.currentTimeMillis() + (session.getCurrentGoods().getVieRedPacketSec() + 5L) * 1000));
        liveSessionRedpacketRepository.save(sessionRedpacket);
    }


    /**
     * 领取群发红包
     */
    public void getRedpacket(LiveSessionRedpacket sessionRedpacket, WebUser user) {

        Requires.isTrue(sessionRedpacket.getSendLen() < sessionRedpacket.getLen(), "红包已领取完");
        Requires.isTrue(System.currentTimeMillis() < sessionRedpacket.getEndTime().getTime(), "超出领取时间");
        checkRedpacketDraw(sessionRedpacket, user);

        synchronized (SyncLock.getLock(String.valueOf(sessionRedpacket.getId()))) {
            sessionRedpacket = queryChannelService.findOne(LiveSessionRedpacket.class, sessionRedpacket.getId());
            Requires.isTrue(sessionRedpacket.getSendLen() < sessionRedpacket.getLen(), "红包已领取完");
            checkRedpacketDraw(sessionRedpacket, user);

            LiveSession session = sessionRedpacket.getLiveSession();
            LiveSessionUserRedPacket userRedPacket = new LiveSessionUserRedPacket()
                    .setLiveSession(session)
                    .setType(sessionRedpacket.getType())
                    .setUser(user)
                    .setCreateTime(new Timestamp(System.currentTimeMillis()))
                    .setPrice(sessionRedpacket.getPrice())
                    .setLiveSessionGoods(session.getCurrentGoods())
                    .setLiveSessionRedpacket(sessionRedpacket);
            liveSessionUserRedpacketRepository.save(userRedPacket);

            sessionRedpacket.setSendLen(sessionRedpacket.getSendLen() + 1);
            liveSessionRedpacketRepository.save(sessionRedpacket);
        }
    }


}
